<?php
Yii::import('dao.bid.BidDao');
Yii::import('dao.bidAuction.AuctionLimitDao');

class BidAuction extends CFormModel
{

    const LOOP_INTERVAL = 500000;

    public $auctionId;

    public $customerId;

    public $timeOutLeft;

    protected $_retry = false;

    protected $_auction;

    protected $_bidAfterInfo;

    protected $_bidDao;

    protected $_env = array();

    public function setEnv($env, $on = true)
    {
        $this->_env[$env] = $on;
        return $this;
    }

    public function isEnv($env)
    {
        return array_key_exists($env, $this->_env) && $this->_env[$env];
    }

    public function init()
    {
        $this->_bidDao = new BidDao();
    }

    public function bid()
    {
        do {
            $retry = time() < $this->timeOutLeft;
            if (! $this->isEnv('parentTransaction')) {
                $transaction = Yii::app()->db->beginTransaction();
            }
            try {
                
                if (! $this->validate()) {
                    throw new CException(Yii::t('yii', 'Bid Auction Validate'));
                }
                if (Yii::app()->lock->get('BID_AUCTION_LOCK:' . $this->auctionId, $this->_auction->end_time + 10)) {
                    usleep(self::LOOP_INTERVAL);
                    continue;
                }
                
                $this->bidBase();
                
                if (! $this->isEnv('parentTransaction')) {
                    $transaction->commit();
                }
                return true;
            } catch (Exception $e) {
                $retry = false;
                if (! $this->isEnv('parentTransaction')) {
                    $transaction->rollback();
                }
                if ($this->isEnv('throw')) {
                    throw new CException(Yii::t('yii', $e->getMessage()));
                }
                return false;
            } finally{
                Yii::app()->lock->del('BID_AUCTION_LOCK:' . $this->auctionId);
            }
        } while ($retry);
        if ($this->isEnv('throw')) {
            throw new CException(Yii::t('yii', 'Bid Auction Time Out'));
        }
        return false;
    }

    public function bidBase()
    {
        $endTime = $this->_auction->end_time;
        
        $lastTime = $this->_auction->end_time - time();
        
        switch (true) {
            case $lastTime <= 10:
                $endTime = time() + 11;
                break;
            case $lastTime <= 5:
                $endTime = time() + 6;
                break;
        }
        // update auction
        $updataAuctionSQL = '
            UPDATE  
                auction_pool
            SET
                auction_pool.winner_price   = @bid_after_winner_price   := auction_pool.winner_price + auction_pool.winner_price_increment,
                auction_pool.winner_id      = @bid_after_winner_id      := ' . $this->customerId . ',
                auction_pool.winner_time    = @bid_after_winner_time    := ' . time() . ',
                auction_pool.end_time       = @bid_after_end_time       := ' . $endTime . '
            WHERE
                auction_pool.auction_id = ' . $this->auctionId . '
        ';
        $updataAuction = Yii::app()->db->createCommand($updataAuctionSQL)->execute();
        
        
        
        $getBidAfterSQL = '
            SELECT
                @bid_after_winner_price AS bid_after_winner_price,
                @bid_after_winner_id AS bid_after_winner_id,
                @bid_after_winner_time AS bid_after_winner_time,
                @bid_after_end_time AS bid_after_end_time
        ';
        
        $this->_bidAfterInfo = Yii::app()->db->createCommand($getBidAfterSQL)->queryRow();
        $this->_bidAfterInfo['bidHistoryId'] = 0;
        
        //判断获取MySQL变量是否为空 ，为空抛出异常
        if(empty($this->_bidAfterInfo['bid_after_winner_price'])){
            throw new CException(Yii::t('yii', 'getBidAfterSql error,bid_after_winner_price is null'));
        }
        
        //bid cost
        $this->_bidDao->customerId = $this->customerId;
        $cash = $this->_bidDao->getBidModel('cash')->getBid();
        if ($cash > 0) {
            $bidModel = $this->_bidDao->getBidModel('cash');
        } else {
            $bidModel = $this->_bidDao->getBidModel('promo');
        }
        $bidModel->setEnv('parentTransaction');
        $setBid = $bidModel->setBid(- 1, 'BID_AUCTION', 'BID_AUCTION:' . $this->auctionId);
        if (! $setBid) {
            $this->addErrors($bidModel->getErrors());
            throw new CException(Yii::t('yii', 'Bid Auction Cost Bid Error'));
        }
        
        $bidModel->component('auctionCost')->setAucitonId($this->auctionId)->cost();
               
        //add customer Cost
        
        $this->_bidAfterInfo['bidHistoryId'] = $bidModel->getLastHistoryId();
        
        $action = 'BID';
        if ($this->isEnv('autoBid')) {
            $action = 'AUTO_BID';
        }
        
        //add bid log
        
        $auctionBidLog = new AuctionBidLog();
        $auctionBidLog->attributes = array(
            'auction_id' => $this->auctionId,
            'winner_price' => $this->_bidAfterInfo['bid_after_winner_price'],
            'winner_name' => $this->_customer->user_name,
            'action' => $action,
            'customer_id' => $this->customerId,
            'bid_source_type' => $bidSourceAction,
            'amount' => -1,
            'bid_type' => strtoupper($this->_bidType),
            'action_history' => 'COST',
            'note' => 'BID_AUCTION:' . $this->auctionId,
            'created' => time(),
        );
        
        $auctionBidLog->save();
        // add limit
        $auctionLimit = new AuctionLimitDao();
        $auctionLimit->auctionId = $this->auctionId;
        $auctionLimit->customerId = $this->customerId;
        $auctionLimit->add($this->_auction->end_time);
      
        
    }

    public function getBidAfterInfo()
    {
        return $this->_bidAfterInfo;
    }

    public function getAuctionInfo()
    {
        $auctionPool = new AuctionPool();
        $auctionInfo = $auctionPool->findByAttributes(array(
            'auction_id' => $this->auctionId,
            'status' => 1
        ));
        $this->_auction = $auctionInfo;
        return $this->_auction;
    }

    public function rules()
    {
        return array(
            array(
                'auctionId,customerId',
                'required'
            ),
            array(
                'auctionId',
                'hasAuction'
            )
        );
    }

    public function hasAuction($attribute, $params)
    {
        $auctionInfo = $this->getAuctionInfo();
        if (empty($auctionInfo)) {
            $this->addError($attribute, 'ext');
        }
    }

    public function auctionLimit($attribute = 'auctionId', $params = null)
    {
        $this->_auctionLimitAr->winner_id = $this->customerId;
        $limitList = $this->_auctionLimitAr->limitData();
        if ($limitList->limitNumber >= 5) {
            $this->addError($attribute, 'limit Count');
            return false;
        }
        $auctionList = explode(',', $limitList->limitList);
        if (in_array($this->auctionId, $auctionList)) {
            $this->addError($attribute, 'limit winner');
            return false;
        }
        return true;
    }

    public function auctionLock($attribute = 'auctionId', $params = null)
    {
        $expire = $this->_auction->end_time + 1;
        $lockName = 'bidAuctionLock:' . $this->auctionId;
        $lock = Yii::app()->dbRedis->setnx($lockName, $expire);
        Yii::app()->dbRedis->expire($lockName, $expire);
        if (empty($lock)) {
            return false;
        }
        return true;
    }

    public function unAuctionLock()
    {
        $lockName = 'bidAuctionLock:' . $this->auctionId;
        return Yii::app()->dbRedis->delete($lockName);
    }

    protected function bidProcess()
    {}

    public function bid1()
    {
        $valid = $this->validate();
        if (! $valid) {
            return ! $this->hasErrors();
        }
        
        // valid
        $expire = time() + 1;
        $transaction = Yii::app()->db->getCurrentTransaction();
        if (empty($transaction)) {}
        
        $transaction = Yii::app()->db->beginTransaction();
        
        try {
            
            do {
                $this->_retry = time() < $expire;
                $valid = $this->auctionLimit();
                if (! $valid) {
                    return ! $this->hasErrors();
                }
                $valid = $this->auctionLock();
                if (! $valid) {
                    continue;
                }
                
                $this->unAuctionLock();
                return true;
                usleep(100000);
            } while ($this->_retry);
            $this->addError('system', 'time_out');
        } catch (Exception $e) {
            $transaction->rollback();
            $this->addError('system', $e->getMessage());
        }
    }
}